<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted, computed, watch, nextTick, useSlots, ComputedRef, markRaw } from "vue";
import { useUpdateDomHeight, useCancelUpdateDomHeight } from "@/hooks/useCommon";
import { cloneDeep } from "@pureadmin/utils";
import type { TableRowCtx } from "px-ui";
import { useStartLoading } from "@/hooks/useCommon";
import ReTableColumnMultistage from "@/components/ReTableColumnMultistage";

const tableBoxRef = ref<HTMLElement | null>(null);
const tableRef = ref<CommonTable.TableRefType>(null);
const pageNum = ref(1);
const pageSize = ref(20);
const totalPage = ref(0);
const gap = 110;
const defaultHeight = 648;
const tableHeight = ref<number>(defaultHeight);
const commonSelection = reactive<Array<any>>([]);
const tableKey = ref<string>(Math.random().toString(36).slice(2, 10));

const tableData = defineModel({ type: Array as PropType<Array<any>>, default: () => [] });
const loading = defineModel("loading", { type: Boolean, default: false });

let defaultSearchParams = {
  pageNum: 1,
  pageSize: 20
};
let currentSearchParams = {};

type Props = {
  /**  模式 default 默认  stripe 条纹 */
  mode?: CommonTable.modeType;
  /** 是否暗黑模式 默认 false */
  dark?: boolean;
  /**  是否需要默认请求数据 */
  immediate?: boolean;
  /**  是否调用接口请求数据 */
  isAxiosRequire?: boolean;
  /**  是否单选 */
  highlightCurrentRow?: boolean;
  /**  是否多选 */
  selection?: boolean;
  /**  是否需要索引 */
  indexNeed?: boolean;
  /**  索引名称 */
  indexLabel?: string;
  /**   table高度 */
  height?: number | string;
  /**   table最高高度 */
  maxHeight?: number;
  /**   操作栏宽度 */
  operationWidth?: number;
  /**   同 px-empty image-size */
  imageSize?: number;
  /**  table 数据 */
  list?: Array<any> | null | undefined;
  /**  operation 配置 */
  operationOptions?: Array<CommonTable.operationOptionItemType> | null | undefined;
  /** operation label */
  operationLabel?: string;
  /**  是否需要 pagination 默认为true */
  pagination?: boolean;
  /**  pagination 参数*/
  paginationProps?: Record<string, any> | null | undefined;
  /**  table 表头数据 */
  tableCloumns?: Array<CommonTable.TableCloumnsType> | null | undefined;
  /**  table 参数 */
  params?: Record<string, any> | null | undefined;
  /**  同 px-table row-class-name */
  rowClassName?: CommonTable.RowCallBackType | string | null | undefined;
  /**  同 px-table row-key */
  rowKey?: CommonTable.RowKeyCallBackType | null | undefined;
  /**  同 px-table row-key */
  selectable?: CommonTable.SelectableCallBackType | null | undefined;
  /**  同 px-table header-cell-style 属性 */
  headerCellStyle?: CommonTable.CellCallBackType | Record<string, string> | null | undefined;
  /**  同 px-table header-row-style 属性 */
  headerRowStyle?: CommonTable.RowCallBackType | Record<string, string> | null | undefined;
  /**  同 px-table cell-style 属性 */
  cellStyle?: CommonTable.CellCallBackType | Record<string, string> | null | undefined;
  /**  同 px-table row-style 属性 */
  rowStyle?: CommonTable.RowCallBackType | Record<string, string> | null | undefined;
  /**  table请求接口 */
  axiosApi?: CommonTable.AxiosApiCallBackType | null | undefined;
};

const props = withDefaults(defineProps<Props>(), {
  mode: "default",
  dark: false,
  immediate: false,
  isAxiosRequire: true,
  indexNeed: false,
  indexLabel: "序号",
  highlightCurrentRow: false,
  selection: false,
  height: defaultHeight,
  operationWidth: 0,
  imageSize: 80,
  pagination: true,
  operationLabel: "操作",
  params: undefined,
  list: undefined,
  paginationProps: undefined,
  operationOptions: () => [],
  tableCloumns: () => []
});

interface Emits {
  /**  单选chang事件，同 px-table current-change事件 */
  (event: "current-change", val: any): void;
  /**  多选chang事件，同 px-table selection-change事件 */
  (event: "selection-change", val: Array<any>): void;
  /**  排序chang事件，同 px-table sort-change事件 */
  (event: "sort-change", val: CommonTable.SortParams): void;
  /**  调用查询接口获取数据，触发 */
  (event: "data-change", val: any): void;
}

const emits = defineEmits<Emits>();

const tooltipOptions = reactive<any>({
  popperClass: "common-table-overflow-tooltip"
});

const realTableHeight: ComputedRef<number | string> = computed(() => props.height || tableHeight.value);
const bottom_border_height: ComputedRef<string> = computed(() => (props.mode === "stripe" ? "0px" : "1px"));

const restProps: ComputedRef<Record<string, any>> = computed(() => {
  let p = {};
  const height = realTableHeight.value;
  if (props.maxHeight) {
    p = { ...p, maxHeight: props.maxHeight };
  } else {
    p = { ...p, height };
  }
  return p;
});

const indexMethod = (index: number) => {
  return (pageNum.value - 1) * pageSize.value + index + 1;
};

const getRowKeyFn = (row: InstanceType<typeof TableRowCtx>): string | number => {
  if (props.rowKey) {
    const r = props.rowKey(row);
    if (r) {
      return r;
    } else {
      return row.id;
    }
  }
  return row.id;
};

const getSelectableFn = (row: any, index: number) => {
  if (props.selectable) {
    return props.selectable(row, index);
  }
  return true;
};

const rowClassNameFn = (rowData: CommonTable.RowType): any => {
  if (props.rowClassName) {
    return typeof props.rowClassName === "function" ? props.rowClassName(rowData) : props.rowClassName;
  } else if (props.mode === "stripe") {
    const { rowIndex } = rowData;
    return rowIndex % 2 === 0 ? "common-table-stripe" : "common-table-white";
  } else {
    return "";
  }
};

const getCommonStyle = (data: Pick<CommonTable.CellType, "column" | "columnIndex">, flag: boolean = false): Record<string, string> => {
  const {
    column: { type = "" },
    columnIndex
  } = data;
  const style = {};
  const tableCloumnsLength = props.tableCloumns?.reduce((pre, cur) => {
    const { children = [] } = cur;
    if (!!children?.length) {
      pre += children?.length;
    } else {
      pre++;
    }
    return pre;
  }, 0);
  if (!tableCloumnsLength) return style;
  if (props.selection) {
    if (props.indexNeed) {
      if (flag) {
        if ((columnIndex < 2 && (type === "selection" || type === "index")) || columnIndex === tableCloumnsLength + 2) {
          style["text-align"] = "center";
        }
      } else {
        if (columnIndex < 2 || columnIndex === tableCloumnsLength + 2) {
          style["text-align"] = "center";
        }
      }
    } else {
      if (flag) {
        if ((columnIndex === 0 && type === "selection") || columnIndex === tableCloumnsLength + 1) {
          style["text-align"] = "center";
        }
      } else {
        if (columnIndex === 0 || columnIndex === tableCloumnsLength + 1) {
          style["text-align"] = "center";
        }
      }
    }
  } else if (props.indexNeed) {
    if (flag) {
      if ((columnIndex === 0 && type === "index") || columnIndex === tableCloumnsLength + 1) {
        style["text-align"] = "center";
      }
    } else {
      if (columnIndex === 0 || columnIndex === tableCloumnsLength + 1) {
        style["text-align"] = "center";
      }
    }
  }
  return style;
};

const headerCellStyleFn = (cellData: CommonTable.CellType): Record<string, string> => {
  const { column, columnIndex } = cellData;
  const style: any = getCommonStyle({ column, columnIndex }, true);
  style["background-color"] = "var(--px-color-primary-light-9) !important";
  style["height"] = "var(--px-table__cell-height) !important";
  style["font-size"] = "14px";
  if (props.mode === "stripe") {
    // style["background-color"] = "#fff !important";
    style["border-bottom"] = "none";
  }
  if (props.dark) {
    style["background-color"] = "transparent !important";
    style["color"] = "#fff";
  }
  if (props.headerCellStyle) {
    if (typeof props.headerCellStyle === "function") {
      const s = props.headerCellStyle(cellData);
      if (s) {
        return { ...style, ...s };
      } else {
        return style;
      }
    } else {
      return { ...style, ...props.headerCellStyle };
    }
  }
  return style;
};

const headerRowStyleFn = (rowData: CommonTable.RowType): Record<string, string> => {
  const style = {};
  if (props.headerRowStyle) {
    if (typeof props.headerRowStyle === "function") {
      const s = props.headerRowStyle(rowData);
      if (s) {
        return { ...style, ...s };
      } else {
        return style;
      }
    } else {
      return { ...style, ...props.headerRowStyle };
    }
  }
  return style;
};

const cellStyleFn = (cellData: CommonTable.CellType): Record<string, string> => {
  const { column, columnIndex, rowIndex } = cellData;
  const style: any = getCommonStyle({ column, columnIndex });
  if (props.mode === "stripe") {
    style["border-bottom"] = "none";
    if (props.dark) {
      if (rowIndex % 2 === 0) {
        style["background-color"] = "rgba(172, 227, 255, 0.1)";
      } else {
        style["background-color"] = "transparent !important";
      }
    }
  }
  if (props.dark) {
    style["color"] = "#fff";
  }
  if (props.cellStyle) {
    if (typeof props.cellStyle === "function") {
      const s = props.cellStyle(cellData);
      if (s) {
        return { ...style, ...s };
      } else {
        return style;
      }
    } else {
      return { ...style, ...props.cellStyle };
    }
  }
  return style;
};

const rowStyleFn = (rowData: CommonTable.RowType): Record<string, string> => {
  const { rowIndex } = rowData;
  const style = {};
  if (props.mode === "stripe") {
    // style["background-color"] = "var(--px-color-primary-light-9)";
    if (props.dark) {
      if (rowIndex % 2 === 0) {
        style["background-color"] = "rgba(172, 227, 255, 0.1)";
      } else {
        style["background-color"] = "transparent !important";
      }
    }
  }
  if (props.rowStyle) {
    if (typeof props.rowStyle === "function") {
      const s = props.rowStyle(rowData);
      if (s) {
        return { ...style, ...s };
      } else {
        return style;
      }
    } else {
      return { ...style, ...props.rowStyle };
    }
  }
  return style;
};

/**
 * @description: 重新获取table数据
 * @param {*} cb 成功后的回调
 * @param {*} flag 是否刷新table
 * @return {*}
 */
const queryTableData = async (cb?: (data: Array<any>) => void, flag?: boolean): Promise<void> => {
  if (!props.axiosApi) return null;
  useStartLoading(loading);
  const params = getParams();
  let res = null;
  if (props.isAxiosRequire) {
    res = await props.axiosApi(params, err => {
      loading.value = false;
    });
  }
  loading.value = false;
  emits("data-change", res);
  tableData.value.splice(0);
  totalPage.value = 0;
  if (res?.total) {
    const data = initTable(res);
    tableData.value.push(...data);
  } else if (!props.pagination && res?.length) {
    tableData.value.push(...(res as Array<any>));
  }
  await nextTick();
  flag && (tableKey.value = Math.random().toString(36).slice(2, 10));
  cb && cb(tableData.value);
};

const initTable = (data_o: any): Array<any> => {
  const { data = [], total = 0 } = data_o;
  totalPage.value = total;
  return data;
};

const getParams = (): Record<string, any> => {
  let sp = cloneDeep(defaultSearchParams);
  let csp = cloneDeep(currentSearchParams);
  let psp = { pageNum: pageNum.value, pageSize: pageSize.value };
  return props.pagination ? { ...sp, ...csp, ...psp } : { ...csp };
};

const currentChangeHandler = (val: any): void => {
  emits("current-change", val);
};

const selectionChangeHandler = (val: any): void => {
  commonSelection.splice(0);
  commonSelection.push(...val);
  emits("selection-change", val);
};

const sortChangeHandler = (val: CommonTable.SortParams): void => {
  emits("sort-change", val);
};

const handleSizeChange = async (val: number): Promise<void> => {
  pageSize.value = val;
  await queryTableData();
};

const handleCurrentChange = async (val: number): Promise<void> => {
  pageNum.value = val;
  await queryTableData();
};

const clearSort = (): void => {
  tableRef.value.clearSort();
};

const clearSelection = (): void => {
  tableRef.value.clearSelection();
};

watch(
  () => props.params,
  async val => {
    pageNum.value = 1;
    pageSize.value = 20;
    totalPage.value = 0;
    currentSearchParams = cloneDeep(val);
    await nextTick();
    await queryTableData();
  },
  { immediate: props.immediate, deep: true }
);

watch(
  () => props.list,
  async val => {
    if (val !== undefined) {
      tableData.value.splice(0);
      val?.length && tableData.value.push(...val);
    }
  },
  { immediate: true, deep: true }
);

onMounted(async () => {
  await useUpdateDomHeight(tableHeight, tableBoxRef, gap, defaultHeight);
});

onUnmounted(() => useCancelUpdateDomHeight());

defineExpose({
  el: tableRef,
  commonSelection,
  loading,
  queryTableData,
  getParams,
  clearSort,
  clearSelection
});
</script>

<template>
  <div ref="tableBoxRef" class="common-table">
    <px-table
      ref="tableRef"
      :key="tableKey"
      v-loading="loading"
      :class="[dark ? 'table-dark' : '']"
      border
      :stripe="mode === 'stripe'"
      :data="[...tableData]"
      lazy
      style="width: 100%"
      :tooltip-options="tooltipOptions"
      :row-key="getRowKeyFn"
      :row-class-name="rowClassNameFn"
      :header-cell-style="headerCellStyleFn"
      :header-row-style="headerRowStyleFn"
      :cell-style="cellStyleFn"
      :row-style="rowStyleFn"
      v-bind="{ ...restProps, ...$attrs }"
      @current-change="currentChangeHandler"
      @selection-change="selectionChangeHandler"
      @sort-change="sortChangeHandler"
    >
      <px-table-column
        v-if="selection"
        class-name="common-column-cell"
        type="selection"
        :selectable="getSelectableFn"
        align="center"
        fixed
        reserve-selection
        width="60"
      />
      <px-table-column v-if="indexNeed" :label="indexLabel" type="index" :index="indexMethod" fixed width="60" class-name="common-column-cell" />
      <ReTableColumnMultistage v-for="item in tableCloumns" :key="item?.prop" class-name="common-column-cell" :column="item" />
      <px-table-column
        v-if="useSlots()?.operation || operationOptions?.length"
        :label="operationLabel"
        fixed="right"
        align="center"
        :width="operationWidth || (operationOptions.length > 1 ? operationOptions.length * 50 : 80)"
        class-name="common-column-cell"
      >
        <template #default="scope">
          <slot v-if="useSlots()?.operation" name="operation" :scope="scope" />
          <div v-else-if="operationOptions?.length" :style="{ height: 'var(--px-table__cell-div-height)' }">
            <px-tooltip
              v-for="item in operationOptions.filter(item => !item?.isHidden || !item?.isHidden(scope))"
              :key="item.content"
              effect="dark"
              :content="item.content"
              :hide-after="0"
            >
              <px-button
                v-has="item.auth || null"
                :style="{ height: 'var(--px-table__cell-div-height)' }"
                :disabled="item?.isDisabled ? item?.isDisabled(scope) : item.disabled"
                :type="item.type || 'primary'"
                :size="item.size || 'large'"
                :icon="markRaw(item.icon)"
                text
                @click="item.click(scope)"
              />
            </px-tooltip>
          </div>
        </template>
      </px-table-column>
      <template v-slot:empty>
        <slot v-if="useSlots()?.empty" name="empty" />
        <px-empty v-else class="px-empty-common" :image-size="imageSize">
          <slot v-if="useSlots()?.emptyBottom" name="emptyBottom" />
        </px-empty>
      </template>
    </px-table>
    <div v-if="pagination" class="pagination">
      <px-pagination
        v-model:current-page="pageNum"
        v-model:page-size="pageSize"
        :page-sizes="[20, 40, 60, 100]"
        background
        layout="total, sizes, prev, pager, next, jumper"
        :total="totalPage"
        v-bind="{ ...paginationProps }"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

<style lang="scss">
.common-table-overflow-tooltip {
  min-width: 120px;
  max-width: 360px;
}
</style>
<style scoped lang="scss">
.common-table {
  height: 100%;

  .table-dark {
    --px-table-border-color: transparent;
    --px-table-border: none;
    --px-table-text-color: #bdbdbe;
    --px-table-header-text-color: #bdbdbe;
    --px-table-row-hover-bg-color: transparent;
    --px-table-current-row-bg-color: transparent;
    --px-table-header-bg-color: transparent;
    --px-table-bg-color: transparent;
    --px-table-tr-bg-color: transparent;
    --px-table-expanded-cell-bg-color: transparent;

    :deep(.common-table-white) {
      background-color: transparent !important;
    }
  }

  .pagination {
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    margin-top: 16px;
  }

  .common-column-cell {
    .colum-item {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
}

:deep(.px-table--striped) {
  .px-table__body tr.px-table__row--striped td.px-table__cell {
    background-color: var(--px-color-primary-light-9);
  }
}

:deep(.px-table--isfixedHeader) {
  .px-table__header-wrapper .px-table-fixed-column--left,
  .px-table__header-wrapper .px-table-fixed-column--right {
    background-color: var(--px-color-primary-light-9) !important;
  }
}

:deep(.px-table__cell) {
  height: var(--px-table__cell-height);
  padding: var(--px-table__cell-padding);

  .cell {
    height: var(--px-table__cell-div-height);
    line-height: var(--px-table__cell-div-height);
  }

  .px-button.is-text.px-tooltip__trigger {
    padding: 0 4px;
  }
}

:deep(.px-table__inner-wrapper) {
  &::before {
    height: v-bind(bottom_border_height);
  }
}

:deep(.common-table-white) {
  background-color: #fff !important;
}

:deep(.px-table__empty-text) {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;

  .px-empty {
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

:deep(.px-table) {
  z-index: 9;

  .cell.px-tooltip {
    width: 100% !important;
    min-width: 40px;
  }
}

:deep(.px-button--large) {
  --px-button-size: 24px;
}

:deep(.px-button) {
  height: 24px;
}
</style>
